মাল্টিথ্রেডিং হলো এমন একটি প্রক্রিয়া, যার মাধ্যমে একটি প্রোগ্রাম একাধিক কাজ বা টাস্ক একযোগে সম্পাদন করতে পারে। C++ এ মাল্টিথ্রেডিং ব্যবহারের জন্য C++11 থেকে <thread>
লাইব্রেরি যুক্ত করা হয়েছে, যা একাধিক থ্রেড তৈরি এবং পরিচালনা করার সুবিধা দেয়। মাল্টিথ্রেডিং ব্যবহার করে প্রোগ্রামের কার্যক্ষমতা বৃদ্ধি করা যায় এবং একই সময়ে বিভিন্ন কাজ সম্পন্ন করা যায়।
C++ এ <thread>
লাইব্রেরি ব্যবহার করে থ্রেড তৈরি করা যায় এবং এর জন্য std::thread
ক্লাস ব্যবহার করা হয়। একটি থ্রেড তৈরি করতে একটি ফাংশন বা lambda
ফাংশন দেওয়া হয়, যা সেই থ্রেডে চালিত হয়।
#include <iostream>
#include <thread>
using namespace std;
void printNumbers() {
for (int i = 1; i <= 5; i++) {
cout << "Number: " << i << endl;
}
}
void printCharacters() {
for (char ch = 'A'; ch <= 'E'; ch++) {
cout << "Character: " << ch << endl;
}
}
int main() {
thread t1(printNumbers); // প্রথম থ্রেডে printNumbers ফাংশন
thread t2(printCharacters); // দ্বিতীয় থ্রেডে printCharacters ফাংশন
t1.join(); // থ্রেড t1 শেষ হওয়া পর্যন্ত অপেক্ষা
t2.join(); // থ্রেড t2 শেষ হওয়া পর্যন্ত অপেক্ষা
cout << "Main function ends." << endl;
return 0;
}
বর্ণনা:
t1
এবং t2
তৈরি করা হয়েছে, যেখানে t1
printNumbers
ফাংশন এবং t2
printCharacters
ফাংশন সম্পন্ন করবে।t1.join()
এবং t2.join()
দ্বারা মেইন থ্রেড অন্য থ্রেডগুলো শেষ হওয়া পর্যন্ত অপেক্ষা করে।উদাহরণ আউটপুট (পরিকল্পিত নয়):
Number: 1
Character: A
Number: 2
Character: B
...
Main function ends.
#include <iostream>
#include <thread>
using namespace std;
int main() {
// একটি ল্যাম্বডা ফাংশন ব্যবহার করে থ্রেড তৈরি করা
thread t([]() {
for (int i = 1; i <= 5; i++) {
cout << "Lambda Thread: " << i << endl;
}
});
t.join(); // থ্রেড t শেষ হওয়া পর্যন্ত অপেক্ষা
cout << "Main function ends." << endl;
return 0;
}
বর্ণনা:
t
নামে একটি থ্রেড তৈরি করা হয়েছে, যা একটি ল্যাম্বডা ফাংশন ব্যবহার করে কাজ করছে।মাল্টিথ্রেডিংয়ে একাধিক থ্রেড একই ডেটা অ্যাক্সেস করলে ডেটা রেস (data race) সমস্যা দেখা দিতে পারে। std::mutex
ব্যবহার করে একটি থ্রেড এক সময়ে একটি মাত্র থ্রেডকে ডেটা অ্যাক্সেস করার অনুমতি দেয়, যা ডেটা রেস প্রতিরোধ করে।
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int counter = 0;
mutex mtx; // mutex অবজেক্ট
void increment() {
for (int i = 0; i < 1000; i++) {
mtx.lock(); // mutex লক করা
counter++;
mtx.unlock(); // mutex আনলক করা
}
}
int main() {
thread t1(increment);
thread t2(increment);
t1.join();
t2.join();
cout << "Final Counter: " << counter << endl;
return 0;
}
বর্ণনা:
counter
ভ্যারিয়েবল t1
এবং t2
থ্রেডের মাধ্যমে ১০০০ বার করে ইনক্রিমেন্ট করা হয়েছে।mtx.lock()
এবং mtx.unlock()
ব্যবহার করে একটি থ্রেডকে counter
ভ্যারিয়েবল অ্যাক্সেসের সময় লক করা হয়েছে, যাতে ডেটা রেস এড়ানো যায়।join
করার জন্য প্রস্তুত কিনা তা পরীক্ষা করে।#include <iostream>
#include <thread>
using namespace std;
void printMessage() {
cout << "Detached thread running..." << endl;
}
int main() {
thread t(printMessage);
if (t.joinable()) {
t.detach(); // ব্যাকগ্রাউন্ডে থ্রেড চালানো
}
cout << "Main function ends." << endl;
return 0;
}
বর্ণনা:
t
থ্রেড detach()
করে ব্যাকগ্রাউন্ডে চালানো হয়েছে।joinable()
ফাংশন ব্যবহার করে দেখা হয়েছে যে থ্রেড join
করার যোগ্য কিনা।mutex
ব্যবহার করা জরুরি।mutex
ব্যবহারে সাবধান থাকা দরকার, কারণ ভুল ব্যবহারে ডেডলক (deadlock) হতে পারে।join
বা detach
করে বন্ধ করা উচিৎ।<thread>
লাইব্রেরি ব্যবহার করে পরিচালনা করা হয়।std::thread
ক্লাস ব্যবহার করে থ্রেড তৈরি করা হয় এবং join()
বা detach()
দিয়ে থ্রেড ম্যানেজ করা হয়।মাল্টিথ্রেডিংয়ের মাধ্যমে প্রোগ্রামিং আরও কার্যকরী এবং দ্রুত করা সম্ভব। তবে, মাল্টিথ্রেডিং ব্যবহারে সতর্ক থাকা দরকার, কারণ এটি যথাযথ ম্যানেজমেন্ট না থাকলে ডেটা রেস এবং ডেডলক সমস্যা সৃষ্টি করতে পারে।
থ্রেড হলো প্রোগ্রামের একটি সাব-ইউনিট, যা প্রোগ্রামের নির্দিষ্ট অংশকে স্বতন্ত্রভাবে এক্সিকিউট (run) করতে সক্ষম করে। C++11 থেকে থ্রেড ব্যবস্থাপনার জন্য thread
লাইব্রেরি যুক্ত করা হয়েছে, যা একই প্রোগ্রামের একাধিক অংশকে একসঙ্গে চালাতে দেয়। থ্রেড ব্যবহারের মাধ্যমে প্রোগ্রামের পারফরম্যান্স বৃদ্ধি করা যায়, কারণ এটি একাধিক কাজকে সমান্তরালভাবে (parallel) চালাতে সহায়ক।
C++ এ thread
লাইব্রেরি ব্যবহার করে থ্রেড তৈরি ও পরিচালনা করা হয়। একটি থ্রেড চালাতে একটি ফাংশন, মেম্বার ফাংশন, বা ল্যাম্বডা এক্সপ্রেশন দেওয়া যায়, যা থ্রেড চালানোর সময় পারফর্ম করবে।
cpp
Copy code
#include <iostream>
#include <thread>
using namespace std;
// একটি সাধারণ ফাংশন, যা থ্রেডের মাধ্যমে চালানো হবে
void printMessage() {
cout << "Hello from thread!" << endl;
}
int main() {
// একটি নতুন থ্রেড তৈরি করা এবং ফাংশন চালানো
thread t(printMessage);
// মূল থ্রেডে অন্য কাজ করা
cout << "Hello from main thread!" << endl;
// থ্রেড শেষ করা (join করা)
t.join();
return 0;
}
বর্ণনা:
printMessage
নামে একটি ফাংশন তৈরি করা হয়েছে, যা থ্রেডের মাধ্যমে চালানো হবে।thread t(printMessage);
লাইনের মাধ্যমে একটি থ্রেড তৈরি করে printMessage
ফাংশন চালানো হয়েছে।t.join();
ব্যবহার করে থ্রেড শেষ হওয়ার জন্য অপেক্ষা করা হয়েছে।join()
মেথড ব্যবহার করে মূল থ্রেড অন্য থ্রেড শেষ হওয়ার জন্য অপেক্ষা করে।detach()
মেথড ব্যবহার করে থ্রেডকে প্রধান থ্রেড থেকে আলাদা করে দেওয়া হয়, ফলে থ্রেড নিজে থেকেই কাজ সম্পন্ন করতে পারে।joinable()
মেথড ব্যবহার করে থ্রেড join
করা যায় কিনা তা পরীক্ষা করা যায়।#include <iostream>
#include <thread>
using namespace std;
void printMessage() {
cout << "Hello from detached thread!" << endl;
}
int main() {
thread t(printMessage);
// থ্রেড detach করা
t.detach();
// মূল থ্রেডে অন্য কাজ করা
cout << "Hello from main thread!" << endl;
return 0;
}
বর্ণনা:
detach()
মেথড ব্যবহার করে থ্রেডটি আলাদা করা হয়েছে, ফলে printMessage
ফাংশন থ্রেড নিজে থেকেই কাজ সম্পন্ন করবে এবং মূল থ্রেড আলাদাভাবে চলতে থাকবে।থ্রেডের মাধ্যমে ফাংশনে প্যারামিটার পাস করা সম্ভব। প্যারামিটার পাস করার সময় std::ref
ব্যবহার করে রেফারেন্স পাস করা যেতে পারে।
#include <iostream>
#include <thread>
using namespace std;
void add(int a, int b) {
cout << "Sum: " << a + b << endl;
}
int main() {
// প্যারামিটার সহ থ্রেড চালানো
thread t(add, 5, 10);
t.join();
return 0;
}
বর্ণনা:
add
ফাংশন দুটি ইনটিজার প্যারামিটার গ্রহণ করে।thread t(add, 5, 10);
এর মাধ্যমে add
ফাংশন ৫ এবং ১০ প্যারামিটার হিসেবে পেয়ে থ্রেডে চালিত হয়েছে।থ্রেড ব্যবস্থাপনায় কখনও কখনও বিভিন্ন থ্রেডের মধ্যে ডেটা সঠিকভাবে ব্যবহারের জন্য সিঙ্ক্রোনাইজেশন প্রয়োজন হয়। C++ এ মিউটেক্স (Mutex) ব্যবহার করে থ্রেড সিঙ্ক্রোনাইজেশন করা হয়, যাতে একাধিক থ্রেড একই ডেটা একসাথে মডিফাই করতে না পারে।
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex mtx; // একটি মিউটেক্স তৈরি করা হয়েছে
void printMessage(const string& msg) {
mtx.lock(); // মিউটেক্স লক করা
cout << msg << endl;
mtx.unlock(); // মিউটেক্স আনলক করা
}
int main() {
thread t1(printMessage, "Hello from thread 1");
thread t2(printMessage, "Hello from thread 2");
t1.join();
t2.join();
return 0;
}
বর্ণনা:
mutex
তৈরি করা হয়েছে এবং mtx.lock()
ও mtx.unlock()
এর মাধ্যমে ডেটা অ্যাক্সেসের সময় লক করা হয়েছে, যাতে দুটি থ্রেড একই সময়ে একসাথে মেমোরি অ্যাক্সেস না করতে পারে।join
বা detach
না করলে থ্রেড সম্পন্ন না হওয়ার ঝুঁকি থাকে, যা মেমোরি লিক হতে পারে।thread
লাইব্রেরির মাধ্যমে থ্রেড ব্যবস্থাপনা করা যায় এবং join
, detach
মেথড ব্যবহার করে থ্রেড পরিচালনা করা যায়।থ্রেড ব্যবহারে প্রোগ্রামিং আরও কার্যকর এবং সময়সাশ্রয়ী হয়, তবে সঠিক ব্যবস্থাপনা নিশ্চিত করা জরুরি।
C++ এ মাল্টিথ্রেডিং ব্যবহারের মাধ্যমে একাধিক কাজ একযোগে সম্পন্ন করা যায়। থ্রেড তৈরি, ম্যানেজ করা, এবং থ্রেডের সাথে যোগাযোগ করার জন্য <thread>
লাইব্রেরি এবং std::thread
ক্লাস ব্যবহার করা হয়। প্রতিটি থ্রেড আলাদা একটি ফাংশন বা কোডের অংশ সম্পাদন করে।
C++ এ থ্রেড তৈরি করতে std::thread
ক্লাস ব্যবহার করা হয়। থ্রেড তৈরি করার সময় ফাংশন, মেম্বার ফাংশন, বা ল্যাম্বডা ফাংশন প্রদান করা হয়, যা থ্রেডটি সম্পাদন করবে।
#include <iostream>
#include <thread>
using namespace std;
void printMessage() {
cout << "Hello from thread!" << endl;
}
int main() {
thread t(printMessage); // থ্রেড t তৈরি এবং printMessage ফাংশন কল
t.join(); // থ্রেড t শেষ হওয়া পর্যন্ত অপেক্ষা
cout << "Main function ends." << endl;
return 0;
}
বর্ণনা:
t
নামে একটি থ্রেড তৈরি করা হয়েছে, যা printMessage
ফাংশন সম্পাদন করবে।t.join()
দিয়ে থ্রেডটি শেষ হওয়া পর্যন্ত মেইন থ্রেড অপেক্ষা করবে।আউটপুট:
Hello from thread!
Main function ends.
ল্যাম্বডা ফাংশনের মাধ্যমে থ্রেড তৈরি করলে কোড আরও সংক্ষিপ্ত ও সহজ হয়।
#include <iostream>
#include <thread>
using namespace std;
int main() {
thread t([]() {
cout << "Hello from lambda thread!" << endl;
});
t.join(); // থ্রেড t শেষ হওয়া পর্যন্ত অপেক্ষা
cout << "Main function ends." << endl;
return 0;
}
বর্ণনা:
t
তৈরি করা হয়েছে, যা একটি ছোট কাজ সম্পন্ন করছে।join()
এর মাধ্যমে থ্রেড শেষ হওয়া পর্যন্ত মেইন থ্রেড অপেক্ষা করছে।কোনো ক্লাসের মেম্বার ফাংশনকেও থ্রেড হিসেবে ব্যবহার করা যায়।
#include <iostream>
#include <thread>
using namespace std;
class Worker {
public:
void printMessage() {
cout << "Hello from member function!" << endl;
}
};
int main() {
Worker worker;
thread t(&Worker::printMessage, &worker); // মেম্বার ফাংশন দিয়ে থ্রেড তৈরি
t.join(); // থ্রেড শেষ হওয়া পর্যন্ত অপেক্ষা
cout << "Main function ends." << endl;
return 0;
}
বর্ণনা:
Worker
ক্লাসের printMessage
মেম্বার ফাংশন থ্রেড t
এর মাধ্যমে কল করা হয়েছে।&Worker::printMessage
এর মাধ্যমে মেম্বার ফাংশন থ্রেডে পাস করা হয়েছে এবং &worker
এর মাধ্যমে সেই ক্লাসের অবজেক্টটি প্রদান করা হয়েছে।join()
মেথডের মাধ্যমে একটি থ্রেড শেষ হওয়া পর্যন্ত অপেক্ষা করা হয়।detach()
মেথড ব্যবহার করে একটি থ্রেড ব্যাকগ্রাউন্ডে চালাতে দেওয়া হয়, এবং মেইন থ্রেড অবাধে চলতে পারে।joinable()
চেক করে যে থ্রেডটি join()
করার জন্য প্রস্তুত কিনা।#include <iostream>
#include <thread>
using namespace std;
void backgroundTask() {
cout << "Background task running..." << endl;
}
int main() {
thread t(backgroundTask);
if (t.joinable()) {
t.detach(); // থ্রেড ব্যাকগ্রাউন্ডে চলে যাবে
}
cout << "Main function ends." << endl;
return 0;
}
বর্ণনা:
t
থ্রেড detach()
মেথড ব্যবহার করে ব্যাকগ্রাউন্ডে চলে গেছে।joinable()
চেক করা হয়েছে যে থ্রেডটি join
করার যোগ্য কিনা।আউটপুট:
Background task running...
Main function ends.
একই প্রোগ্রামে একাধিক থ্রেড তৈরি করে তাদের একযোগে চালানো যায়।
#include <iostream>
#include <thread>
using namespace std;
void task1() {
for (int i = 1; i <= 5; i++) {
cout << "Task 1 - Count: " << i << endl;
}
}
void task2() {
for (int i = 1; i <= 5; i++) {
cout << "Task 2 - Count: " << i << endl;
}
}
int main() {
thread t1(task1); // প্রথম থ্রেড
thread t2(task2); // দ্বিতীয় থ্রেড
t1.join(); // প্রথম থ্রেড শেষ হওয়া পর্যন্ত অপেক্ষা
t2.join(); // দ্বিতীয় থ্রেড শেষ হওয়া পর্যন্ত অপেক্ষা
cout << "Both tasks completed." << endl;
return 0;
}
বর্ণনা:
t1
এবং t2
তৈরি করা হয়েছে, যা task1
এবং task2
ফাংশন চালাবে।t1.join()
এবং t2.join()
দিয়ে উভয় থ্রেড শেষ হওয়া পর্যন্ত মেইন থ্রেড অপেক্ষা করছে।আউটপুট (পরিকল্পিত নয়):
Task 1 - Count: 1
Task 2 - Count: 1
Task 1 - Count: 2
Task 2 - Count: 2
...
Both tasks completed.
mutex
ব্যবহার করা হয়।mutex
ব্যবহারের সময় ডেডলক সমস্যা হতে পারে। তাই সাবধানে lock
এবং unlock
ব্যবহার করতে হবে।join()
বা ব্যাকগ্রাউন্ডে চালানোর জন্য detach()
করা প্রয়োজন।std::thread
ব্যবহার করে থ্রেড তৈরি করা যায়।join()
, detach()
, এবং joinable()
থ্রেড ম্যানেজমেন্টের জন্য ব্যবহৃত হয়।mutex
এবং ডেডলক প্রতিরোধ ব্যবস্থা গ্রহণ করা উচিত।থ্রেড ব্যবহারে কার্যক্ষমতা বৃদ্ধি পায়, তবে এটি ব্যবহারে সতর্ক থাকা উচিত, কারণ থ্রেড পরিচালনার ভুলে প্রোগ্রাম ক্র্যাশ করতে পারে।
মিউটেক্স (Mutex) হলো C++ এর একটি সিঙ্ক্রোনাইজেশন প্রক্রিয়া, যা একাধিক থ্রেডকে সমন্বিতভাবে ডেটা ব্যবহারের সুযোগ দেয়, কিন্তু এক থ্রেড অন্য থ্রেডের কাজ শেষ না হওয়া পর্যন্ত সেই ডেটা অ্যাক্সেস করতে পারে না। মিউটেক্স ব্যবহার করে একাধিক থ্রেডের মধ্যে ডেটা শেয়ার করার সময় ডেটার সামঞ্জস্যতা (consistency) বজায় রাখা যায় এবং ডেটা রেস সমস্যা প্রতিরোধ করা যায়।
সিঙ্ক্রোনাইজেশন একটি প্রক্রিয়া, যা নিশ্চিত করে যে একাধিক থ্রেড একসঙ্গে একই ডেটা অ্যাক্সেস বা মডিফাই করতে পারবে না। যদি সিঙ্ক্রোনাইজেশন না করা হয়, তবে ডেটা রেস নামক সমস্যা হতে পারে, যেখানে একাধিক থ্রেড একসঙ্গে একই ডেটা মডিফাই করতে গেলে ভুল বা অনির্দেশ্য আউটপুট আসতে পারে।
সিঙ্ক্রোনাইজেশন ব্যবহারের প্রয়োজনীয়তা:
C++ এ mutex
লাইব্রেরি থেকে মিউটেক্স ব্যবহৃত হয়। এটি সাধারণত তিনটি মেথড দ্বারা কাজ করে:
true
রিটার্ন করে, ব্যর্থ হলে false
রিটার্ন করে।#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex mtx; // মিউটেক্স তৈরি করা
void printMessage(const string& msg) {
mtx.lock(); // মিউটেক্স লক করা
cout << msg << endl;
mtx.unlock(); // মিউটেক্স আনলক করা
}
int main() {
thread t1(printMessage, "Hello from thread 1");
thread t2(printMessage, "Hello from thread 2");
t1.join();
t2.join();
return 0;
}
বর্ণনা:
mtx.lock()
এবং mtx.unlock()
ব্যবহার করে মিউটেক্স লক এবং আনলক করা হয়েছে, যাতে cout
স্টেটমেন্ট একসঙ্গে একাধিক থ্রেড দ্বারা অ্যাক্সেস না হয়।লকার ব্যবহার করে মিউটেক্সের লক এবং আনলক করা আরও সহজ করা যায়। lock_guard
একটি সিঙ্ক্রোনাইজেশন ক্লাস, যা মিউটেক্স লক করে এবং যখন স্কোপের বাইরে চলে যায়, তখন স্বয়ংক্রিয়ভাবে আনলক করে।
lock_guard
ব্যবহার#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex mtx;
void printMessage(const string& msg) {
lock_guard<mutex> lock(mtx); // lock_guard ব্যবহার করে মিউটেক্স লক
cout << msg << endl;
}
int main() {
thread t1(printMessage, "Hello from thread 1");
thread t2(printMessage, "Hello from thread 2");
t1.join();
t2.join();
return 0;
}
বর্ণনা:
lock_guard
ব্যবহার করে মিউটেক্স লক করা হয়েছে, এবং কাজ শেষে এটি স্বয়ংক্রিয়ভাবে আনলক হয়।unlock()
কল করার দরকার নেই, যা কোডকে আরও সুরক্ষিত এবং পরিষ্কার করে তোলে।ডেডলক হলো এমন একটি অবস্থা যেখানে দুটি বা ততোধিক থ্রেড একে অপরের উপর নির্ভরশীল হয়ে অপেক্ষা করে, ফলে তারা কোন কাজই করতে পারে না। সাধারণত মিউটেক্স ব্যবহারের সময় ডেডলক সমস্যা দেখা দিতে পারে।
ডেডলক প্রতিরোধের জন্য কিছু পদ্ধতি:
try_lock()
ব্যবহার করে ডেডলক প্রতিরোধ করা যায়, কারণ এটি ব্যর্থ হলে false
রিটার্ন করে, ফলে থ্রেডটি অবিরত অপেক্ষা করে না।lock_guard
বা unique_lock
ব্যবহার করে লক করার প্রক্রিয়া সহজ করা যায় এবং স্কোপের বাইরে যাওয়ার সময় এটি স্বয়ংক্রিয়ভাবে আনলক হয়।#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex mtx1, mtx2;
void thread1() {
if (mtx1.try_lock()) {
this_thread::sleep_for(chrono::milliseconds(100)); // সামান্য বিরতি
if (mtx2.try_lock()) {
cout << "Thread 1 is running." << endl;
mtx2.unlock();
}
mtx1.unlock();
}
}
void thread2() {
if (mtx2.try_lock()) {
this_thread::sleep_for(chrono::milliseconds(100)); // সামান্য বিরতি
if (mtx1.try_lock()) {
cout << "Thread 2 is running." << endl;
mtx1.unlock();
}
mtx2.unlock();
}
}
int main() {
thread t1(thread1);
thread t2(thread2);
t1.join();
t2.join();
return 0;
}
বর্ণনা:
try_lock()
ব্যবহার করে থ্রেডে ডেডলক প্রতিরোধ করা হয়েছে।try_lock()
এর মাধ্যমে লক করতে ব্যর্থ হয়, তবে সেটি অবিরত অপেক্ষা না করে ফিরে আসে এবং পরবর্তী কাজ করতে পারে।lock_guard
ও try_lock()
ব্যবহার করে ডেডলক সমস্যার সমাধান করা যায়।মিউটেক্স এবং সিঙ্ক্রোনাইজেশন ব্যবহার করে প্রোগ্রামে ডেটা শেয়ারিং আরও নিরাপদ এবং নির্ভরযোগ্য করা যায়, যা বড় মাল্টিথ্রেডেড প্রোগ্রাম তৈরিতে অত্যন্ত গুরুত্বপূর্ণ।
common.read_more